import { markRaw } from 'vue'
import { nextKey } from '../../util'
import type { FloatPaneHolder, FloatPaneOptions } from './types'

const holders: Record<string, FloatPaneHolder> = {}
let activeHolder: FloatPaneHolder | null = null
const minZIndex = 1000
const maxZIndex = 10000
let activeZIndex = 1000

function openPane(opt: FloatPaneOptions, bBox: number[], defaultHidable: boolean): FloatPaneHolder {
  const h = {
    key: opt.key || nextKey(),
    component: markRaw(opt.component),
    props: opt.props || {},
    events: opt.props || {},
    name: opt.name,
    icon: opt.icon,
    hidable: opt.hidable != null ? opt.hidable : defaultHidable
  } as FloatPaneHolder
  holders[h.key] = h
  activePane(h)
  initialPosition(h, bBox)
  return h
}

function activePane(h: FloatPaneHolder) {
  if (!h.visible) {
    h.visible = true
  }
  if (h === activeHolder) {
    return
  }
  deActiveCurrentPane()
  h.zIndex = activeZIndex + 1
  activeZIndex = h.zIndex
  h.active = true
  activeHolder = h

  if (activeZIndex > maxZIndex) {
    const lst = Object.assign([], Object.values(holders)) as FloatPaneHolder[]
    lst.sort((a, b) => {
      return a.zIndex - b.zIndex
    })
    let i = minZIndex
    for (const e of lst) {
      e.zIndex = i
      i++
    }
    activeZIndex = i
  }
}

function closePane(h: FloatPaneHolder) {
  deActivePane(h)
  delete holders[h.key]
}

function hidePane(h: FloatPaneHolder) {
  h.visible = false
  deActivePane(h)
}

function dragPane(h: FloatPaneHolder, bBox: number[], evt: MouseEvent) {
  h.x = evt.clientX
  h.y = evt.clientY
  const minX = bBox[0]
  const minY = bBox[1]
  const maxX = bBox[2]
  const maxY = bBox[3]
  const m = (evt: MouseEvent) => {
    const dx = evt.clientX - h.x
    const dy = evt.clientY - h.y
    h.x = evt.clientX
    h.y = evt.clientY
    let left = h.left + dx
    left = Math.max(minX, left)
    left = Math.min(maxX, left)
    let top = h.top + dy
    top = Math.max(minY, top)
    top = Math.min(maxY, top)
    h.left = left
    h.top = top
  }
  const u = () => {
    document.removeEventListener('mousemove', m)
    document.removeEventListener('mouseup', u)
  }
  document.addEventListener('mousemove', m)
  document.addEventListener('mouseup', u)
}

function deActivePane(h: FloatPaneHolder) {
  if (h.active) {
    h.active = false
  }
  if (activeHolder !== h) {
    return
  }
  const lst = Object.values(holders)
  let maxZIdx = -1
  let h1: FloatPaneHolder | undefined
  for (const e of lst) {
    if (!e.visible || e === h) {
      continue
    }
    if (maxZIdx < e.zIndex) {
      h1 = e
      maxZIdx = e.zIndex
    }
  }
  if (h1) {
    activePane(h1)
  } else {
    activeHolder = null
  }
}

function deActiveCurrentPane() {
  if (activeHolder) {
    activeHolder.active = false
    activeHolder = null
  }
}

function initialPosition(fh: FloatPaneHolder, bBox: number[]) {
  const minX = bBox[0]
  const minY = bBox[1]
  const maxX = bBox[2]
  const maxY = bBox[3]
  const r = Math.floor(Math.random() * 10.0) / 10.0
  const left = minX + Math.floor(r * (maxX - minX))
  const top = minY + Math.floor(r * (maxY - minY))
  fh.top = top
  fh.left = left
}

export const floatPaneManager = {
  openPane,
  closePane,
  hidePane,
  activePane,
  dragPane
}
